package kibahed.uni.charts;

import ij.measure.CurveFitter;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Shape;
import java.io.File;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import org.apache.commons.math3.stat.StatUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.Range;
import org.jfree.data.general.Dataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.data.xy.YIntervalSeries;
import org.jfree.data.xy.YIntervalSeriesCollection;
import org.jfree.util.ShapeUtilities;



@XmlRootElement
public class ComplexChart extends SimpleChart {

	@XmlTransient
	public List<XYDataPairList> meanDataLists = new LinkedList<XYDataPairList>();

	@XmlTransient
	public List<XYDataPairList> oldDataLists = new LinkedList<XYDataPairList>();

	@XmlElementWrapper
	@XmlAnyElement(lax = true)
	public List<XYDataPairList> savedDataLists = new LinkedList<XYDataPairList>();

	JPanel testPanel;

	JFrame frame;
	XYSeriesCollection dataset;

	public ComplexChart() {

		// drawingMode = AKChart.reportStyle;

	}

	public ComplexChart(Integer style) {
		super(style);
	}

	@Override
	public void generateChart(Integer view) {

		dataPairLists = meanDataLists;
		savedDataLists = dataPairLists;
		prepareChart();
		addDataToChart(meanDataLists);
		renderChart();
		createChart();

	}

	public void generateChartFromFile() {

		
		
		resetDrawingModeAfterLoading();
		createColors();
		createShapes();
		
		// holzhacker methode; sollte automatisiert werden
		chartType = AKChart.lambdaFactorChart;
		
		setDrawingMode(AKChart.reportStyle);
		prepareChart();
		addDataToChart(savedDataLists);
		renderChart();
		createChart();

		File dFile = new File(getPath());
		int n = dFile.listFiles().length;

		saveChart();

	}

	private void prepareChart() {
		frame = new JFrame();
		frame.setSize(800, 800);
		frame.setVisible(true);

		testPanel = new JPanel();
		testPanel.setSize(800, 800);
		testPanel.setLayout(new GridLayout());
		testPanel.setVisible(true);

		frame.add(testPanel);
	}

	private void addDataToChart(List<XYDataPairList> dataList) {
		double l = 0;

		String[] vn = getValueNames();
		String[] fn = getFittedValueNames();

		dataset = new XYSeriesCollection();
		
		for (int i = 0; i < dataList.size(); i++) {

			XYSeries series1 = new XYSeries(vn[i]);
			XYSeries series2 = new XYSeries(fn[i]);
			XYSeries series3 = new XYSeries("TEST" + i);

			double[] xDoubleValues = dataList.get(i).getXValues();
			double[] yDoubleValues = dataList.get(i).getYValues();

			for (int j = 0; j < xDoubleValues.length; j++) {
				series1.add(xDoubleValues[j], yDoubleValues[j]);
			}
			
			CurveFitter cf = new CurveFitter(xDoubleValues, yDoubleValues);
			cf.doFit(CurveFitter.POLY3);

			double[] smoothedVales = getSmoothedValues(yDoubleValues, 1);
			
			// for getting right LAmbda Chart -- ??
			
			// should check chartType
			
			if (chartType == AKChart.lambdaFactorChart) {
				System.out.println("---------- Drawing mode = LambdaFactorChart");
				
				l = yDoubleValues.length / 20;
				
				for (int m = xDoubleValues.length-1; m >= 0; m--) {
					
					if (m % l == 0 || m == yDoubleValues.length - 1) {
						double yV = (double)((int)(smoothedVales[m]+0.5));
						System.out.println("yV = "+yV);
						series2.add(xDoubleValues[m], yV);
					}

					series3.add(xDoubleValues[m], yDoubleValues[m]);
				}
			} else if(drawingMode == AKChart.distanceBetweenToursChart){
					
				for (int m = xDoubleValues.length; m >= 0; m--) {
					if (m % l == 0 || m == yDoubleValues.length - 1)
						series2.add(xDoubleValues[m], cf.getYPoints()[m]);

					series3.add(xDoubleValues[m], yDoubleValues[m]);
				}
				
			} else {

				
				l = yDoubleValues.length / 20;
				for (int m = cf.getXPoints().length - 1; m >= 0; m--) {
					if (m % l == 0 || m == yDoubleValues.length - 1)
						series2.add(xDoubleValues[m], smoothedVales[m]);

					series3.add(xDoubleValues[m], smoothedVales[m]);
				}
			}
			if (drawingMode == AKChart.fittedValuesAndDataPoints) {
				dataset.addSeries(series1);
				dataset.addSeries(series2);
			}

			if (drawingMode == AKChart.onlyFittedValues) {
				dataset.addSeries(series2);
			}

			if (drawingMode == AKChart.onlyDataPoints) {
				dataset.addSeries(series1);
			}
			if (drawingMode == AKChart.trueLinePlusPoints) {

				dataset.addSeries(series3);
				dataset.addSeries(series2);
			}
			if (drawingMode == AKChart.reportStyle) {
				dataset.addSeries(series2);
			}
		}
	}

	private void renderChart() {

		chart = ChartFactory.createXYLineChart("Test", xAxisName, yAxisName,
				dataset, PlotOrientation.VERTICAL, true, false, false);

		XYPlot plot = chart.getXYPlot();
		plot.setBackgroundPaint(Color.WHITE);
		plot.setDomainGridlinePaint(Color.GRAY);
		plot.setRangeGridlinePaint(Color.GRAY);

		XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer() {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			protected boolean isLinePass(int pass) {
				return pass == 1;
			}

			@Override
			protected boolean isItemPass(int pass) {
				return pass == 0;
			}
		};

		XYLineAndShapeRenderer reportRenderer = new XYLineAndShapeRenderer();

		plot.setRenderer(renderer);

		XYItemRenderer itemRenderer = plot.getRenderer();
		BasicStroke lineStroke = new BasicStroke(10);
		BasicStroke pointStroke = new BasicStroke(5);

		BasicStroke s = new BasicStroke(10, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND);
		BasicStroke r = new BasicStroke(15);

		BasicStroke test = new BasicStroke(2.0f, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND, 3.0f, new float[] { 6.0f, 6.0f }, 0.0f);

		Shape cross = ShapeUtilities.createDiagonalCross(10, 2);

		for (int i = 0; i < dataset.getSeriesCount(); i++) {
			int c = i;

			if (drawingMode == AKChart.reportStyle) {

				plot.setRenderer(reportRenderer);
				reportRenderer.setSeriesLinesVisible(i, true);
				reportRenderer.setSeriesShapesVisible(i, true);
				reportRenderer.setAutoPopulateSeriesShape(true);
				reportRenderer.setAutoPopulateSeriesStroke(true);

				reportRenderer.setSeriesStroke(i, new BasicStroke(3));
				// reportRenderer.setSeriesStroke(i, test);
				reportRenderer.setSeriesShape(i, shapes.get(i));
				reportRenderer.setSeriesPaint(i, colors.get(c));
			}

			if (drawingMode == AKChart.trueLinePlusPoints) {

				c = i / 2;
				//

				if (i % 2 == 0) {
					renderer.setSeriesLinesVisible(i, true);
					renderer.setSeriesShapesVisible(i, false);
					// itemRenderer.setSeriesStroke(i, new BasicStroke(2));
					itemRenderer.setSeriesPaint(i, colors.get(c));
				} else {
					renderer.setSeriesShapesVisible(i, true);
					renderer.setSeriesLinesVisible(i, false);
					// itemRenderer.setSeriesStroke(i, new BasicStroke(2));
					itemRenderer.setSeriesPaint(i, colors.get(c));
				}

			} else if (drawingMode == AKChart.fittedValuesAndDataPoints) {
				c = i / 2;
				if (i % 2 == 0) {
					renderer.setSeriesLinesVisible(i, false);
					renderer.setSeriesShapesFilled(i, true);
					itemRenderer.setSeriesStroke(i, pointStroke);
					itemRenderer.setSeriesPaint(i, colors.get(c));
				} else {
					renderer.setSeriesShapesVisible(i, false);
					itemRenderer.setSeriesStroke(i, s);
					itemRenderer.setSeriesPaint(i, colors.get(c));
				}
			} else if (drawingMode == AKChart.onlyFittedValues) {

				renderer.setSeriesShapesVisible(i, false);
				itemRenderer.setSeriesStroke(i, s);
				itemRenderer.setSeriesPaint(i, colors.get(c));

			} else if (drawingMode == AKChart.onlyDataPoints) {

				renderer.setSeriesLinesVisible(i, false);
				renderer.setSeriesShapesFilled(i, true);
				itemRenderer.setSeriesPaint(i, colors.get(c));

			}
		}

	}

	private void createChart() {
		TextTitle t = chart.getTitle();
		t.setText(getTitle());
		t.setFont(getTitleFont());
		chart.setTitle(t);
		
		ValueAxis xAxis = chart.getXYPlot().getDomainAxis();
		ValueAxis yAxis = chart.getXYPlot().getRangeAxis();

		NumberAxis logY = new LogarithmicAxis(yAxis.getLabel());
		
		ValueAxis logX = new LogarithmicAxis(xAxis.getLabel());
		
		//logX.setFixedDimension(Math.E);
		
		// adapting the range of the yAxis
		Range range = chart.getXYPlot().getDataRange(yAxis);
		Range expRange = Range.expand(range, 0.2, 0.2);
		yAxis.setRange(expRange);
		logY.setRange(expRange);
		//logX.setRange(expRange);
		
		xAxis.setTickLabelFont(getxAxisValueFont());
		yAxis.setTickLabelFont(getyAxisValueFont());
		logY.setTickLabelFont(getyAxisValueFont());
		logX.setTickLabelFont(getxAxisValueFont());
			
		xAxis.setLabelFont(getxAxisLabelFont());
		yAxis.setLabelFont(getyAxisLabelFont());
		logY.setLabelFont(getyAxisLabelFont());
		logX.setLabelFont(getxAxisLabelFont());
		
		LegendTitle legendTitle = chart.getLegend();
		legendTitle.setItemFont(getLegendFont());

		xAxis.setLabelPaint(Color.BLACK);
		xAxis.setTickLabelPaint(Color.BLACK);
		yAxis.setLabelPaint(Color.BLACK);
		yAxis.setTickLabelPaint(Color.BLACK);
		logY.setTickLabelPaint(Color.BLACK);
		logX.setTickLabelPaint(Color.BLACK);
		legendTitle.setItemPaint(Color.BLACK);
		
		chart.getXYPlot().setDomainAxis(logX);
		chart.getXYPlot().setRangeAxis(yAxis);
		ChartPanel myChart = new ChartPanel(chart);

		testPanel.add(myChart);
		testPanel.validate();
	}


	public void switchToNextDataPairList()  {
		
		XYDataPairList mean = new XYDataPairList(activeDataList);
		meanDataLists.add(mean);
		dataPairLists.clear();
		data = new XYDataPairList();
		activeDataList = data;
		dataPairLists.add(data);
	}
	
	public void evalMeanDataPoints() {

		System.out.println("Evaluate Mean Values");

		XYDataPairList mean = new XYDataPairList();
		int size = dataPairLists.size();

		System.out.println("------------------------- Size of DataPairList = "
				+ size);

		if (size > 0) {

			List<double[]> singlePointValues = new LinkedList<double[]>();
					
			int s = dataPairLists.get(0).getYValues().length;
			int n = dataPairLists.size();

			for (int i = 0; i < s; i++) {
				double[] y = new double[n];
				for (int j = 0; j < dataPairLists.size(); j++) {
					y[j] = dataPairLists.get(j).getYValues()[i];
				}
				singlePointValues.add(y);
			}

			YIntervalSeriesCollection devDataSet = null;

			if (drawingMode == AKChart.meanAndDeviationLine) {
				devDataSet = new YIntervalSeriesCollection();
			}

			YIntervalSeries series1 = new YIntervalSeries("Series 1");

			for (int i = 0; i < singlePointValues.size(); i++) {
				XYDataPair pair = new XYDataPair();
				double m = StatUtils.mean(singlePointValues.get(i));

				if (drawingMode == AKChart.meanAndDeviationLine) {
					double var = StatUtils.variance(singlePointValues.get(i));
					double std = Math.sqrt(var);

					series1.add(i, m, m - std, m + std);

				} else {
					pair.setxValue(i);
					pair.setyValue(m);
					mean.addXYDataPair(pair);
				}

			}

			

			// double[] sumYValues = new
			// double[dataPairLists.get(0).getYValues().length];
			//
			// for (int i = 0; i < sumYValues.length; i++) {
			//
			// for (XYDataPairList d : dataPairLists) {
			// sumYValues[i] += d.getYValues()[i];
			// }
			//
			// }
			//
			// for (int i = 0; i < sumYValues.length; i++) {
			// XYDataPair pair = new XYDataPair();
			// pair.setxValue(i);
			// double m = sumYValues[i] / size;
			//
			// if (drawingMode == AKChart.meanAndDeviationLine) {
			//
			//
			//
			//
			// } else {
			//
			// pair.setyValue(m);
			// mean.addXYDataPair(pair);
			// }
			// }
		} else {
			mean = dataPairLists.get(0);
		}
		// double minAvgTourLength =
		// mean.getYValues()[mean.getYValues().length-1];
		// calc.setAvgShortestTourLength(minAvgTourLength);
		// System.out.println("AVG Best Tour length ="+minAvgTourLength);
		// calc.createGeneralInformationForReport();

		meanDataLists.add(mean);

		dataPairLists.clear();

		data = new XYDataPairList();
		activeDataList = data;
		dataPairLists.add(data);

	}

	@Override
	public void valdidatePanel() {
		testPanel.validate();
	}
}
